#include "memory.h"
#include "vmm.h"
#include "thread.h"
#include "string.h"
#include "print.h"
#include "debug.h"
#include "string.h"
#include "virtualdevice.h"
#include "binarytranslation.h"
#include "interrupt.h"
#include "global.h"
#include "keyboard.h"
#include "tss.h"
#include "process.h"


uint32_t guest_kernel_page_table_vaddr;
uint32_t guest_kernel_page_table_phyaddr;

struct task_struct* create_vm(char* name) {

   // struct tss* tss_1 = (struct tss*)(0xc0000b0b);
   // //uint16_t tss_size = (uint16_t)sizeof(tss);
   // memset(((void *)(0xc0000b0b + 0x200)), 0xff, 150);
   // tss_1->io_base = 0x200;

   struct vcpu* VCPU = (struct vcpu*)VCPU_PTR;

   struct PIT8253* pit = (struct PIT8253*)VIRTUAL_PIT;
   init8253(pit);
   pit_write_control(0x36, pit);
   pit_write_counter(0x40, 11931, pit);

   struct PIC8259A* pic_master = (struct PIC8259A*)VIRTUAL_PIC_MASTER;
   struct PIC8259A* pic_slave = (struct PIC8259A*)VIRTUAL_PIC_SLAVE;
   pic_master->slave = pic_slave;
   PIC_INIT(pic_master);
   PIC_INIT(pic_slave);

   PIC_writeICW(pic_master, 0, 0x11);
   PIC_writeICW(pic_master, 1, 0x20);
   PIC_writeICW(pic_master, 2, 0x04);
   PIC_writeICW(pic_master, 3, 0x01);

   PIC_writeICW(pic_slave, 0, 0x11);
   PIC_writeICW(pic_slave, 1, 0x28);
   PIC_writeICW(pic_slave, 2, 0x02);
   PIC_writeICW(pic_slave, 3, 0x01);

   pic_master->imr = 0xfc;

   VCPU->pit = pit;
   VCPU->pic = pic_master;
   VCPU->pit_frequency_proportion = 0;
   VCPU->CPL_ = 0x0;

   struct task_struct* thread = process_execute((void*)(0x1500), name);
   VCPU->h_cursor = 0x0;
   VCPU->l_cursor = 0x0;
   VCPU->vm_phy_page_table = thread->pgdir;
   VCPU->shadow_pt_map_ = NULL;

   guest_memory();
   thread->guest_ptr = (uint32_t*)VCPU;
   // thread->pgdir = create_guest_page_dir();
   
   update_tss_esp(thread);
   guest_kernel_page_table_vaddr = (uint32_t)(create_guestkernel_pagetable());
   guest_kernel_page_table_phyaddr = addr_v2p((uint32_t)guest_kernel_page_table_vaddr);
   
   return thread;

}



struct task_struct* create_vcpu(char* name) {
   struct vcpu* VCPU = (struct vcpu*)VCPU_PTR;
   VCPU->edi = VCPU->esi = VCPU->ebp = VCPU->esp = 0;
   VCPU->ebx = VCPU->eax = VCPU->ecx = VCPU->edx = 0;
   VCPU->ds = 0x0;
   VCPU->es = 0x0;
   VCPU->fs = 0x0;
   VCPU->gs = 0x0;
   VCPU->cs = 0x0;
   VCPU->ss = 0x0;
   VCPU->eflags = 0x46;
   VCPU->h_cursor = 0x0;
   VCPU->l_cursor = 0x0;
   // return process_execute((void*)(0x1500), "guest");
   return thread_start(name, 50, vm_entry, "guest");//create thread
   
}

void guest_memory(void) {
   int i = 0x0;
   int num = 0x200;
   uint32_t virtual_address = (uint32_t)vaddrrr, page_physical_address = (uint32_t)((void *)0x01200000);
   // for (; i < num; i++) {
   //    page_table_add((void *)(virtual_address + (i * 0x1000)), (void *)(page_physical_address + (i * 0x1000)));
   // }

   virtual_address = (uint32_t)0xc0900000, page_physical_address = (uint32_t)0x00900000, i = 0x0, num = 0x800;
   for (; i < num; i++) {
      page_table_add((void *)(virtual_address + (i * 0x1000)), (void *)(page_physical_address + (i * 0x1000)));
   }
   
}

// uint32_t* create_guest_page_dir(void) {
//    uint32_t* page_dir_vaddr = get_kernel_pages(1);
//    if (page_dir_vaddr == NULL) {
//       return NULL;
//    }
//    memcpy((uint32_t*)((uint32_t)page_dir_vaddr + 768 * 4), (uint32_t*)(0xfffff000 + 768 * 4), 255 * 4);
//    uint32_t new_page_dir_phy_addr = addr_v2p((uint32_t)page_dir_vaddr);  
//    page_dir_vaddr[1023] = new_page_dir_phy_addr | PG_US_U | PG_RW_W | PG_P_1; 

//    uint32_t* guest_pde_01 = get_kernel_pages(1);
//    uint32_t counter = 1024, i = 0;
//    for (;i < counter;i++) {
//       *(guest_pde_01 + i ) = (0x01200000 + 0x1000 * i) | PG_US_U | PG_RW_W | PG_P_1;
//    };
//    *(guest_pde_01 + 184 ) = (0xb9000) | PG_US_U | PG_RW_W | PG_P_1;
//    uint32_t guest_pde_01_phy_addr = addr_v2p((uint32_t)guest_pde_01);  
//    page_dir_vaddr[0] = guest_pde_01_phy_addr | PG_US_U | PG_RW_W | PG_P_1;
//     //asm volatile ("movl %0, %%cr3" : : "r" (new_page_dir_phy_addr) : "memory");
//    // asm("jmp .");
//    return page_dir_vaddr;
// }

uint32_t* create_guestkernel_pagetable(void) {
   uint32_t* page_dir_vaddr = get_kernel_pages(1);
   if (page_dir_vaddr == NULL) {
      return NULL;
   }
   memcpy((uint32_t*)((uint32_t)page_dir_vaddr + 768 * 4), (uint32_t*)(0xfffff000 + 768 * 4), 255 * 4);
   uint32_t new_page_dir_phy_addr = addr_v2p((uint32_t)page_dir_vaddr);  
   page_dir_vaddr[1023] = new_page_dir_phy_addr | PG_US_S | PG_RW_W | PG_P_0; 
   
   uint32_t* guest_pde_01 = get_kernel_pages(1);
   uint32_t counter = 256, i = 0, i_1 = 0;
   for (;i < counter;) {
      if (i == 0xb8) {
         i +=8;
         continue;
      }
      *(guest_pde_01 + i ) = (0x01100000 + 0x1000 * i_1) | PG_US_U | PG_RW_W | PG_P_1;
      i++;
      i_1++;
   };
   *(guest_pde_01 + 184 ) = (0xb9000) | PG_US_U | PG_RW_W | PG_P_1;
   *(guest_pde_01 + 256 ) = (0x011f8000) | PG_US_U | PG_RW_W | PG_P_1;
   *(guest_pde_01 + 257 ) = (0x011f9000) | PG_US_U | PG_RW_W | PG_P_1;
   
   uint32_t guest_pde_01_phy_addr = addr_v2p((uint32_t)guest_pde_01);  
   page_dir_vaddr[0] = guest_pde_01_phy_addr | PG_US_U | PG_RW_W | PG_P_1;
    //asm volatile ("movl %0, %%cr3" : : "r" (new_page_dir_phy_addr) : "memory");
   // asm("jmp .");
   return page_dir_vaddr;
}




/* 激活页表 */
void page_dir_activate_test(struct task_struct* p_thread) {
   //asm ("jmp .");
   uint32_t pagedir_phy_addr;
   struct vcpu* vm_thread_cpu = p_thread->guest_ptr;
   
   pagedir_phy_addr = addr_v2p((uint32_t)p_thread->pgdir);
   
   if ((((struct vcpu*)p_thread->guest_ptr)->cr3)) {
      uint32_t shadow_pt = NULL;
      if (vm_thread_cpu->shadow_pt_map_) {
         shadow_pt = (uint32_t)find_shadow_page_table(vm_thread_cpu);
      }
      if (shadow_pt) {
         asm volatile ("movl %0, %%cr3" : : "r" (addr_v2p(shadow_pt)) : "memory");   //更新页目录寄存器cr3,使新页表生效
      } else {
         shadow_pt = (uint32_t)create_shadow_page_table(vm_thread_cpu);
         if (shadow_pt) {
            asm volatile ("movl %0, %%cr3" : : "r" (addr_v2p(shadow_pt)) : "memory");   //更新页目录寄存器cr3,使新页表生效
         } else {
            put_str("\nvmm shadow page table error");
            asm("jmp .");
         }
      }
   } else {
      asm volatile ("movl %0, %%cr3" : : "r" (pagedir_phy_addr) : "memory");   //更新页目录寄存器cr3,使新页表生效
   }
   update_tss_esp(p_thread);
}



extern void intr_exit(void);



void vm_entry(void* arg) {

   //asm volatile("jmp .");
   //struct vcpu* VCPU = (struct vcpu*)0xc0053000;
   //struct task_struct* cur = running_thread();

   // cur->self_kstack += sizeof(struct thread_stack); 
   struct intr_stack* proc_stack = get_kernel_pages(1);
   proc_stack->eax = 0x0;
   proc_stack->ecx = 0x0;
   proc_stack->edx = 0x0;
   proc_stack->ebx = 0x0;
   proc_stack->ebp = 0x0;
   proc_stack->esp_dummy = 0x0;
   proc_stack->esi = 0x0;
   proc_stack->edi = 0x0;
   proc_stack->ds = proc_stack->es = proc_stack->fs = 0x33; 
   proc_stack->gs = 0x33;  
   proc_stack->eip = (void *)0x00001500;
   proc_stack->cs = 0x2b;
   proc_stack->esp = (void *)0x00095000;
   proc_stack->ss = 0x33; 
   proc_stack->eflags = 0x00000246;
   //asm volatile("jmp .");
   asm volatile ("movl %0, %%esp; jmp intr_exit" : : "g" (proc_stack) : "memory");
}




